<html>
<head>
  <title>python 网络爬虫</title>
  <basefont face="微软雅黑" size="2" />
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
  <meta name="exporter-version" content="Evernote Windows/307027 (zh-CN, DDL); Windows/6.1.0 (Win32);"/>
  <style>
    body, td {
      font-family: 微软雅黑;
      font-size: 10pt;
    }
  </style>
</head>
<body>
<a name="628"/>
<h1>python 网络爬虫</h1>

<div>
<span><div><a href="http://blog.csdn.net/hjhmpl123/article/details/52921020">http://blog.csdn.net/hjhmpl123/article/details/52921020</a></div><div>Python是一门高级的动态的语言，利用它进行网络数据的抓取是非常方便的。代码的实现非常的精简。</div><div style="margin-top: 1em; margin-bottom: 1em;">在本次python爬虫中，我们将使用到以下python第三方库：</div><div>1.<span style="font-weight: bold;">requests(urllib3)</span> 用于发起http请求，相对于python自带的urllib2模块，更加的pythonic</div><div>2.<span style="font-weight: bold;">redis-py</span> 连接redis数据库，用于保存待抓取的url队列，并实现分布式</div><div>3.<span style="font-weight: bold;">gevent</span> 实现并发抓取，相对于python的threading性能更好，但是有monkey-patch</div><div>4.<span style="font-weight: bold;">pybloom</span> 布隆过滤器，实现url的去重功能</div><div>5.<span style="font-weight: bold;">MySQL-python</span> 存储网页以及提取到的结构化数据</div><div>6.<span style="font-weight: bold;">Chardet</span> 检测网页编码</div><div>7.<span style="font-weight: bold;">Parsel</span> 基于lxml实现的网页解析器，用法更简单，scrapy用的就是这个，速度可以媲美lxml</div><div>8.<span style="font-weight: bold;">Selenium + phantomjs</span> 抓取js网页</div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><font style="font-size: 18pt;"><span style="font-size: 18pt; color: rgb(173, 0, 0); font-weight: bold;">从客户端发起一个http请求到接受到一个response中间经历了哪些过程？</span></font></div><div><br/></div><div><ol><li><span style="line-height: 1.45;">拥有一个用于请求的url</span></li><li><span style="line-height: 1.45;">dns解析，将url中包含的host提取，由dns解析成ip地址 和 端口号</span></li><li>根据 ip地址 和 端口号 以及host 进行tcp/ip链接，绑定到特定的主机上</li><li>向连接的主机发送 http请求， 优先发送headers， 如果拥有body部分，在发送body部分</li><li>接收 主机的http响应</li></ol><div><br/></div></div><div><br/></div><div><br/></div><div><span style="color: rgb(28, 51, 135); font-size: 18pt;">构建我们的第一个请求</span></div><div>Requests库的用法非常的简单，简单的两句就可以抓取一个网页：</div><div style="box-sizing: border-box; padding: 8px; font-size: 12px; border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);"><div><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">url = '</span><a href="http://www.example.com'/" style="font-size: 9pt; background-color: rgb(251, 250, 248); font-family: Monaco; color: rgb(51, 51, 51);">http://www.example.com'</a><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">;</span></div><div><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">r = requests.get(url)</span></div></div><div>Dns解析，tcp/ip链接，发送http请求我们完全不需要去考虑，requests已经帮我们都实现了。有兴趣的同学，可以参考python内置模块httplib</div><div><br/></div><div>只要成功请求，就会返回一个response对象，否则就会抛出一个异常，requests的所有异常都继承自<font style="font-size: 12pt;"><span style="color: rgb(173, 0, 0); font-size: 12pt; font-weight: bold;">requests.exceptions.RequestException</span></font>.类。</div><div><br/></div><div>        我们可以通过<span style="font-weight: bold;">r.status_code</span>来查看网页的返回码，正常网页的返回码是200，如果返回的是一个已经不存在的网页，状态码就是400+， 如果是服务器错误，就是500+。默认情况下是不会返回300+的状态码的，因为requests会自动为我们重定向。如果要禁用重定向，传入<span style="font-weight: bold;">allow_redirects=False</span>关键词参数即可。在我们的爬虫进行网络数据抓取的时候，建<font style="font-size: 14pt;"><span style="color: rgb(173, 0, 0); font-size: 14pt; font-weight: bold;">议大家最好禁用重定向，因为重定向会非常的耗时</span></font>，比如存在dns解析，tcp/ip链接，然后再发起http请求，之后等待响应。</div><div><br/></div><div>        在http请求的时候，requests会给我们带上一个headers，里面有一些默认的参数值。可以通过response.request.headers进行查看，下面我们以请求百度首页为例，看看headers会默认带上哪些参数。</div><div>{‘accept-encoding’: ‘gzip, deflate’, ‘accept’: ‘<span style="font-style: italic;">/</span>’, ‘user-agent’: ‘python-requests/版本号’ }</div><div>这是requests默认给我们带上的参数。</div><div><br/></div><div>’accept-encoding’向服务器表明了我们客户端可以接受的数据编码类型，只要服务器探测到客户端的请求的头部中有这个字段，那么就会发送相应的编码的数据过去。默认情况下我们接受到的response都是经过gzip压缩过的，因为目前来说gzip压缩的比率是最好的，这样可以减少数据的传输，提高性能，减少延迟。</div><div><br/></div><div>accept’向服务器表明了我们客户端可以接受哪种数据类型，<span style="font-style: italic;">/</span>表示我们可以接受任何类型。但事实上并不是这样，大部分时候我们只需要接受html文本即可。</div><div><br/></div><div>User-agent’ 相信大部分写过爬虫的都对这个不会陌生，user-agent用来向服务器表明客户端的身份。有一些反爬虫较为严格的服务器会根据user-agent来决定是够返回响应数据，大部分时候，我们的做法是去获取一大批浏览器的user-agent,这种做法可以给我们提供不少的帮助。</div><div><br/></div><div><br/></div><div><span style="color: rgb(173, 0, 0); font-size: 14pt;">前面我们使用了requests.get(url)这种方法来请求url，在实际使用中你就会发现，这很容易被禁止掉。所以我们需要做一些设置。</span></div><div style="box-sizing: border-box; padding: 8px; font-size: 12px; border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);"><div><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">headers = {'user-agent': 'xxxx',</span></div><div><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">            'host': 'xxx',</span></div><div><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">            'accept': 'xxx',</span></div><div><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">            'accept-charset': 'xxx',</span></div><div><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">            'accept-language': 'xxx',</span></div><div><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">            'referer': 'xxxx',}</span></div></div><div>1.<span style="font-weight: bold;">user-agent</span>用户代理，百度谷歌一下随便找</div><div>2.<span style="font-weight: bold;">host</span>请求url的host, 可使用urlparse模块</div><div>3.<span style="font-weight: bold;">accept</span>如果只能接受html，就写成text/html，具体参考可去w3school上去看详细内容，也可以看看《http权威指南》</div><div>4.<span style="font-weight: bold;">accept-charset</span>建议统一为utf-8,python里面的字符编码问题谁用谁知道</div><div>5.<span style="font-weight: bold;">accept-language</span>如果请求中文页面就统一为zh-cn</div><div>6.<span style="font-weight: bold;">referer</span>:请求url的来源页面，举个列子，我们在浏览器在通过连接点击下一个页面，就会有referer头部，这对服务器来说是友好的。</div><div><br/></div><div>最后加上requests中的一些参数，我们最后的请求如下：</div><div style="box-sizing: border-box; padding: 8px; font-size: 12px; border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);"><div><span style="background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 12px;">import requests</span></div><div><br/></div><div><span style="background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 12px;">headers = {'user-agent': &quot;Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/50.0.2657.3+Safari/537.36&quot;,</span></div><div><span style="background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 12px;">       'host': url_host,</span></div><div><span style="background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 12px;">       'accept': 'text/html',</span></div><div><span style="background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 12px;">       'accept-charset': 'utf-8',</span></div><div><span style="background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 12px;">       'accept-language': 'zh-cn',</span></div><div><span style="background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 12px;">       'referer': referer_url,}</span></div><div><span style="background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 12px;">#如果想要再加上一些其他的请求头部或者完善请求头部，推荐书籍《http权威指南》</span></div><div><br/></div><div><span style="background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 12px;">requests.get(url=url,</span></div><div><span style="background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 12px;">         headers=headers,</span></div><div><span style="background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 12px;">         timeout=5,</span></div><div><span style="background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 12px;">         allow_redirects=False)</span></div></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><font style="font-size: 18pt;"><span style="color: rgb(173, 0, 0); font-size: 18pt; font-weight: bold;">如何解析网页并提取我们需要的数据</span></font></div><div><br/></div><div>我们采用requests这个库进行一个网页请求</div><div style="box-sizing: border-box; padding: 8px; font-size: 12px; border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);"><div><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">r = requests.get('</span><a href="https://www.example.com'/" style="font-size: 9pt; background-color: rgb(251, 250, 248); font-family: Monaco; color: rgb(51, 51, 51);">https://www.example.com'</a><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">;, headers, **kwargs )</span></div></div><div>假设返回的是200的响应。一般情况下我们请求的都是静态的html纯文本网页，采用r.text即可获得我们想要的网页内容。requests会自动为我们解压缩以及进行网页字符解码。默认情况下，requests会采用响应headers中的指定的字符编码进行解码。</div><div><br/></div><div><font style="font-size: 16pt;"><span style="color: rgb(173, 0, 0); font-size: 16pt; font-weight: bold;">解决了网页字符编码问题</span></font></div><div><img src="python 网络爬虫_files/Image.png" type="image/png" data-filename="Image.png"/></div><div>如上图所示，这是一个非常标准的http response头部，现在我们重点看<span style="font-weight: bold;">content-type</span>那一行，在这一行中，我们可以看到’charset=utf-8’，这就说明了网页是采用’utf-8’字符编码的，requests也会采用’utf-8’为网页进行解码，完全没有任何问题。</div><div>其实在目前大部分的网页中(可以用4个9表示)， 是给出了网页对应的字符编码的，如下图所示:</div><div><img src="python 网络爬虫_files/Image [1].png" type="image/png" data-filename="Image.png"/></div><div>很明显，我们可以看到网页的字符编码是’utf-8’，所以我们就可以自己写一个函数，采用正则去提取这个编码：</div><div><br/></div><div><br/></div><div><font color="#AD0000" style="font-size: 16pt;"><span style="color: rgb(173, 0, 0); font-size: 16pt; font-weight: bold;">结构化数据</span></font></div><div><br/></div><div>        推荐使用parsel模块。parsel模块其实就是对lxml的一个封装，但是它使用起来非常的简单。另外，api指南比lxml更简洁易懂。当然，也可以使用beautifulsoup，但是它的解析网页速度不如lxml。至于解析网页是采用css还是xpath，随意。我个人喜欢xpath，另外，css选择器的底层实现就是xpath</div><div style="box-sizing: border-box; padding: 8px; font-size: 12px; border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);"><div><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">from parsel import Selector</span></div><div><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">sel = Selector(html=response.text, type='html')</span></div><div><br style="font-family: Monaco; font-size: 9pt; color: rgb(51, 51, 51);"/></div><div><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">#在提取网页结构化数据的时候，可以使用下面的语句</span></div><div><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">field1 = sel.xpath(xpath路径).extract()</span></div><div><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">field2 = sel.xpath(xpath路径).extract()</span></div><div><br style="font-family: Monaco; font-size: 9pt; color: rgb(51, 51, 51);"/></div><div><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">#在提取网页链接的时候，可以使用下列的语句</span></div><div><span style="font-size: 9pt; background-color: rgb(251, 250, 248); color: rgb(51, 51, 51); font-family: Monaco;">links = sel.xpath(xpath路径).re(正则表达式语句)</span></div></div><div><br/></div><div><font color="#AD0000" style="font-size: 16pt;"><b>保存数据</b></font></div><div><span>    <span>    </span></span></div><div><span>    <span>    </span></span>对于html网页（主要用于缓存）和结构化数据的保存，我选择了MySQL， 当然也可以使用NoSQL的产品， 比如mongodb；对于网页中提取出的url，我们可以保存到本地的文件中，也可以保存到MySQL或者mongodb中等但为了高效，我选择了内存数据库redis，相对来说redis的永久存储性能要弱于MySQL等产品，但他的查询非常的高效。</div><div><br/></div><div><span style="-en-paragraph: true;">python的MySQL的接口安装：</span></div><div style="box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &quot;Courier New&quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902); background-position: initial initial; background-repeat: initial initial;-en-codeblock:true;"><div>pip install MySQL-python</div></div><div style="-en-paragraph: true; margin-top: 1em; margin-bottom: 1em;"><span style="-en-paragraph: true;">python的redis接口：</span></div><div style="box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &quot;Courier New&quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902); background-position: initial initial; background-repeat: initial initial;-en-codeblock:true;"><div>pip install redis-py</div></div><div><span style="line-height: 1.45;">python接口的布隆过滤器：</span><br/></div><div style="box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &quot;Courier New&quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902); background-position: initial initial; background-repeat: initial initial;-en-codeblock:true;"><div>pip install pybloom</div></div><div><span style="-en-paragraph: true;">bloom filter是用来过滤重复的url的，我们从网页中提出的url，有很多是我们已经抓取过的，对于这部分url就要去重，那么bloom filter就是最好的选择，因为它的查重的速度是O(1)。也就是说，你已经抓取了100万条url，现在给你一条新的url，你要判断是否抓取过。采用最普通的方法就是for循环，这需要O(n)的时间；再进一步，我们可以选择集合，因为python的集合是用hash的，但是有一个问题，要把100万条url都放到集合中，这是非常消耗内存的，如果有两百万，三百万，甚至几千万呢？一般的计算机是承受不住的。</span></div><div><span style="-en-paragraph: true;">而bloom filter查重是一个常数时间，而且，他内存消耗率很低，这不正是我们想要的吗？但是它也有一个缺点，会存在误判的情况。就是说，bloom filter瞄过一眼的肯定不会判断错误，但是它可能会把不在bloom filter中的url判断会存在。这种错误率，也称为‘假阳性’，是可以计算出来的。我们可以控制在万分之一左右，这种错误率是可以接受的。</span></div><div><br/></div><div><br/></div><div><font color="#AD0000" style="font-size: 14pt;"><b>假设html我们需要保存的网页(unicode类型)， item是我们需要保存的结构化数据（dict类型）， urls是我们需要保存的url列表（list类型）。</b></font></div><div><br/></div><div style="box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &quot;Courier New&quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902); background-position: initial initial; background-repeat: initial initial;-en-codeblock:true;"><div>import zlib</div><div>import MySQLdb</div><div>class MySQLConnect(object):</div><div>    &quot;&quot;&quot;</div><div>    建立到MySQL的连接</div><div>    &quot;&quot;&quot;</div><div>    #我们先建立到mysql的连接</div><div>    def __init__(self, host=host, password=password,</div><div>                 username=username, database=database,</div><div>                 charset=charset):</div><div>        self.connect = MySQLdb.connect(host=host, user=username,</div><div>                                       passwd=password, db=database</div><div>                                       charset=charset)</div><div>        self.cousor = self.connect.cursor()</div><div><br/></div><div>    def save(self, item, html):</div><div>        result_item = (item(字段1), item(字段2), item(字段3)...)</div><div>        self.cousor.execute(&quot;&quot;&quot;insert into item_table</div><div>                               (字段1, 字段2,.....)</div><div>                               values (%s, %s,....)&quot;&quot;&quot;, result_item)</div><div><br/></div><div>        #首先将html进行压缩，在进行存储</div><div>        compress_html = zlib.compress(html.encode('utf-8'))</div><div>        html_result = (url, compress_html)</div><div>        self.cousor.execute(&quot;&quot;&quot;insert into html_table</div><div>                               (字段1, 字段2)</div><div>                               values (%s, %s)&quot;&quot;&quot;, html_result)</div><div>        self.connect.commit()</div><div><br/></div></div><div><br/></div><div><br/></div><div><span style="-en-paragraph: true;">代码非常简单，其中我虚构了两张表，item_table和html_table，用来存储结构化数据和html网页。当然我们也可以把响应的头部也存储起来，以待更新数据时向服务验证网页是否发生改变。</span></div><div><span style="-en-paragraph: true;">而对于urls列表的存储我们采用下面的方法：</span></div><div><br/></div><div style="box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &quot;Courier New&quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902); background-position: initial initial; background-repeat: initial initial;-en-codeblock:true;"><div>import redis</div><div>import urlparse</div><div>import json</div><div><br/></div><div>from pybloom import BloomFilter</div><div>class MyRedis(myconnection.RedisConnect):</div><div>    &quot;&quot;&quot;</div><div>    redis</div><div>    &quot;&quot;&quot;</div><div>    def __init__(self, host=host, port=port, db=db):</div><div>        self.pool = redis.ConnectionPool(host=host, port=port, db=db)</div><div>        self.r = redis.StrictRedis(connection_pool=self.pool)</div><div><br/></div><div>        try:</div><div>            print u'正在初始化,请稍后.....'</div><div>            f = open('/home/root/bloomfilter.txt')#尝试打开保存bloomfilter的文件</div><div>        except IOError:</div><div>            print 'create a new bloomfilter without file'</div><div>            #如果打开失败，说明不存在这个文件，就重新创建一个bloom filter</div><div>            self.bloomfilter = BloomFilter(capacity=1000000,</div><div>                                           error_rate=0.00001)</div><div>        else:</div><div>            print u'从文件中加载bloom filter'</div><div>            self.bloomfilter = BloomFilter.fromfile(f)</div><div><br/></div><div>    def bloom_filter_url(self, urls, baseurl):</div><div>        &quot;&quot;&quot;</div><div>        将unbloom_url_queue这个队列中的url过滤,取出未抓取的url到url_queue中</div><div>        baseurl用于讲urls列表中的相对url补全为绝对url</div><div>        &quot;&quot;&quot;</div><div>            parse_url = urlparse.urlparse(baseurl)</div><div>            #提取domain，比如‘<a href="https://www.example.com/">https://www.example.com</a>’</div><div>            domain = '://'.join((parse_url.scheme, parse_url.netloc))</div><div>            new_list = []</div><div>            for url in urls:                    </div><div>                #这一步就可以排除所有不在domain下的url,比如domain是<a href="http://www.example.com/">www.example.com</a></div><div>                #那么‘<a href="https://www.qweasd.com’这个域名以及该域名下的所有url/">https://www.qweasd.com’这个域名以及该域名下的所有url</a>就会被排除</div><div>                if (parse_url.netloc not in url) and (parse_url.scheme in url):</div><div>                    pass</div><div>                else:</div><div>                    #这一步判断url是否为完整的url</div><div>                    if domain not in url:</div><div>                        url = ''.join([domain, url])</div><div>                    if '#' in url:</div><div>                        #如果url中存在#号，就删除它</div><div>                        url = urlparse.urldefrag(url)[0]</div><div>                        #url中可能存在转义字符,统一转化为原始字符,比如将%7e转化为~</div><div>                        url = urllib.unquote(url)</div><div>                    if not self.bloomfilter.add(url):#判断url是否在bloomfilter中</div><div>                        new_item = json.dumps({'url': url,</div><div>                                            'base_url': baseurl})</div><div>                        new_list.append(new_item)</div><div>            else:</div><div>                self.r.rpush('url_queue', new_list)</div><div><br/></div></div><div><br/></div><div><font color="#AD0000" style="font-size: 14pt;"><b>网页下载，提取结构化数据，保存网页和数据，去重url并进行保存。一个爬虫完整的步骤已经结束了。</b></font></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><font style="font-size: 16pt; color: rgb(173, 0, 0);"><b>协程</b></font></div><div><span>    <span>    </span></span>在写爬虫程序的时候，我们希望能够并发的抓取，而不是一次只抓取一个url。对于高并发的抓取，目前有三种方案：多进程，多线程，协程(python3.x)。论性能来说，协程最佳(异步执行)，tornado就是采用了协程。</div><div><span>    <span>    </span></span>于是我就采用了轻量级的协程gevent,gevent会自动为我们自动切换协程，这样在等待网络IO的时候，就可以尽最大可能的减少时间的损耗。其实爬虫的性能瓶颈主要在于IO，而传统的python多线程是阻塞，如果遇到IO阻塞了，就会非常的消耗时间，因为他不会自动的切换线程运行。</div><div><br/></div><div style="box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &quot;Courier New&quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902); background-position: initial initial; background-repeat: initial initial;-en-codeblock:true;"><div>import gevent</div><div>import requests</div><div><br/></div><div>def request(url):</div><div>    r = requests.get(url)</div><div>    return r.staus_code</div><div><br/></div><div>g1 = gevent.Greenlet(request, url)</div><div>g2 = gevent.Greenlet(request, url)</div><div>g1.start()</div><div>g2.start()</div><div>gevent.joinall((g1, g2))</div><div><br/></div></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div></span>
</div></body></html> 